package com.lc.admin.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.lc.admin.bean.*;
import com.lc.admin.constants.CheckInConstants;
import com.lc.admin.constants.RetreatConstant;
import com.lc.admin.dto.*;
import com.lc.admin.mapper.*;
import com.lc.admin.service.CheckInService;
import com.lc.admin.service.WorkFlowService;
import com.lc.admin.utils.CodeUtil;
import com.lc.admin.utils.StringUtils;
import com.lc.admin.utils.UserThreadLocal;
import com.lc.admin.vo.CheckInItem;
import com.lc.admin.vo.CheckInVo;
import io.lettuce.core.AbstractRedisAsyncCommands;
import org.activiti.engine.HistoryService;
import org.activiti.engine.history.HistoricTaskInstance;
import org.activiti.engine.history.HistoricTaskInstanceQuery;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;

@Transactional
@Service
public class CheckInServiceImpl implements CheckInService {
    @Autowired
    private ElderMapper elderMapper;

    @Autowired
    private CheckInMapper checkInMapper;

    @Autowired
    AccraditationRecordMapper accraditationRecordMapper;

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private HistoryService historyService;

    @Autowired
    private WorkFlowService workFlowService;

    @Transactional
    @Override
    public void checkIn(CheckInDto checkInDto) {
        // 这条记录是被驳回的，在养老顾问的待办中能查询到这条，然后重新提交这个任务到 护理主管评估
        if(StringUtils.isNotEmpty(checkInDto.getTaskId())) {
            workFlowService.complete(checkInDto.getTaskId(), "提交护理主管审批",null);
            return;
        }

        // 1、检查老人是否已经在申请中，根据身份证号码或手机号码判断
        int count = elderMapper.findCountByPhoneAndCardNo(checkInDto.getElderDto());
        if (count > 0) {
            // 存在
            throw new RuntimeException("该老人已存在入住申请");
        }
        // 2、保存老人信息
        ElderDto elderDto = checkInDto.getElderDto();
        // 图片
        String url1 = checkInDto.getUrl1();   // 近照
        String url2 = checkInDto.getUrl2();   // 身份证正面
        String url3 = checkInDto.getUrl3();   // 身份证反面

        if (StringUtils.isEmpty(url1)) {
            throw new RuntimeException("老人近照不能为空");
        }
        if (StringUtils.isEmpty(url2)) {
            throw new RuntimeException("老人身份证正面不能为空");
        }
        if (StringUtils.isEmpty(url3)) {
            throw new RuntimeException("老人身份证反面不能为空");
        }
        elderDto.setImage(url1 + ";" + url2 + ";" + url3);

        // age
        String otherApplyInfo = checkInDto.getOtherApplyInfo();
        ElderDto elderDto1 = JSONObject.parseObject(otherApplyInfo, ElderDto.class);
        elderDto.setAge(elderDto1.getAge());
        // sex
        elderDto.setSex(elderDto1.getSex());
        // status   默认是入住中
        elderDto.setStatus(ElderDto.Status.IN.getValue());
        elderMapper.save(elderDto);

        // 3、保存入住信息
        CheckIn checkIn = new CheckIn();
        String checkInCode = CodeUtil.generateCode(CheckInConstants.CHECK_IN_PREFIX, redisTemplate, 0);
        checkIn.setCheckInCode(checkInCode);
        checkIn.setTitle(elderDto.getName() + "的入住申请");
        checkIn.setElderId(elderDto.getId());
        // 当前登录的用户  ThreadLocal

        String userJson = UserThreadLocal.getSubject();
        LoginUserDetails user = JSONObject.parseObject(userJson, LoginUserDetails.class);
        checkIn.setCounselor(user.getRealName());
        checkIn.setApplicat(user.getNickName());
        checkIn.setDeptNo(user.getDeptNo());
        checkIn.setApplicatId(user.getId() + "");

        checkIn.setFlowStatus(CheckIn.FlowStatus.APPLY.getCode());
        checkIn.setStatus(CheckIn.Status.APPLICATION.getCode());
        checkIn.setOtherApplyInfo(checkInDto.getOtherApplyInfo());

        checkInMapper.save(checkIn);

        // 4、准备流程变量
        Map<String, Object> map = new HashMap<>();
        // user0：养老顾问  只有一个员工
        map.put("user0", user.getId());
        // user1：护理部门的主管  只有一个员工
        User user1 = userMapper.findUsernameByDeptNo(RetreatConstant.NURSING_DEPT_CODE);
        map.put("user1", user1.getId());
        // user2: 院长
        User user2 = userMapper.findUsernameByDeptNo(RetreatConstant.CENTER_CODE);
        map.put("user2", user2.getId());
        // user3: 养老顾问  第一步的养老顾问即可
        map.put("user3", user.getId());
        // user4: 法务专员
        User user4 = userMapper.findUsernameByDeptNo(RetreatConstant.LEGAL_DEPT_CODE);
        map.put("user4", user4.getId());
        // 设置额外的变量，在查询我的待办条件需要使用，可以理解为是activity的全局变量
        // 单据编号RZ  QJ
        map.put("code", checkInCode);
        // 单据类别
        map.put("type", CheckInConstants.RZ);    // 参考PendingTaskVo类
        // 流程状态
        map.put("status", 1);   // 申请中，参考PendingTaskVo类

        // 设置额外的变量，在查询我的待办封装结果需要使用，可以理解为是activity的全局变量
        map.put("checkInId", checkIn.getId());
        map.put("title", checkIn.getTitle());
        map.put("user0Name", user.getRealName());
        // 业务id
        // 如果这个值唯一，查询任务会更方便，不需要携带额外的条件
        String bussinessId = "bussiness:" + checkIn.getId();

        // 5、保存审核记录
        AccraditationRecord accraditationRecord = AccraditationRecord.builder()
                .opinion("自动通过")
                .type(3)
                .approverId(0L)
                .approverName("自动通过，没有审核人")
                .approverNameRole("自动通过，没有审核人")
                .nextApproverId(user1.getId())
                .nextApprover(user1.getRealName())
//                .nextApproverRole()
                .bussniessId(bussinessId)
                .currentStep("养老顾问(" + user.getNickName() + ")提交申请")
                .nextStep("护理主管(" + user1.getNickName() + ")评估")
                .stepNo("0")
                .auditStatus(1)
                .handleType(0)
                .build();

        accraditationRecordMapper.save(accraditationRecord);

        // 启动工作流
        workFlowService.startProcess("Process_1", bussinessId, map);

    }

    @Override
    public CheckInVo checkInDetails(String assigneeId, String code, Integer flowStatus, String taskId) {
        CheckInVo checkInVo = new CheckInVo();
        // 根据code查询checkIn的数据
        CheckIn checkIn = checkInMapper.findByCode(code);
        CheckInItem checkInItem = new CheckInItem();
        BeanUtils.copyProperties(checkIn, checkInItem);

        checkInVo.setCheckIn(checkInItem);

        // checkIn中有elderId，可以查询老人信息
        ElderDto elder = elderMapper.findById(checkIn.getElderId());
        checkInItem.setElderDto(elder);

        // 获取审核记录
        // 1、申请人必须是我自己
        // 2、审核过的和没审核的应该都可以查看详情
//        HistoricTaskInstanceQuery hisQuery = historyService.createHistoricTaskInstanceQuery();
//        hisQuery.taskAssignee(assigneeId);
//        List<HistoricTaskInstance> list = hisQuery.list();

        // 审核记录
        // 使用的是同一个流程，我们为了区分是设置了不同的bussinessId的
        List<AccraditationRecord> ars = accraditationRecordMapper.findByBussinessId("bussiness:"+checkIn.getId());
        checkInVo.setAccraditationRecords(ars);

        checkInVo.setType(CheckInConstants.RZ.toString());
        checkInVo.setNextApprover(ars.get(ars.size()-1).getNextApprover());

        return checkInVo;
    }

    @Override
    public void review(CheckInReviewDto checkInReviewDto) {
        // 1、更新checkIn表
        CheckIn checkIn =  new CheckIn();
        checkIn.setId(checkInReviewDto.getId());
        checkIn.setFlowStatus(CheckIn.FlowStatus.REVIEW.getCode());
        checkIn.setReviewInfo(JSONObject.toJSONString(checkInReviewDto));
        checkInMapper.update(checkIn);

        // 2、完成任务
        workFlowService.complete(checkInReviewDto.getTaskId(), "护理主管审批-同意",null);

        // 3、保存审核记录
        LoginUserDetails user = UserThreadLocal.getUser();
        // 查询院长信息
        User user2 = userMapper.findUsernameByDeptNo(RetreatConstant.CENTER_CODE);

        AccraditationRecord accraditationRecord = AccraditationRecord.builder()
                .opinion("护理主管审批-同意")
                .type(3)
                .approverId(user.getId())
                .approverName(user.getRealName())
                .nextApproverId(user2.getId())
                .nextApprover(user2.getRealName())
                .bussniessId("bussiness:"+checkInReviewDto.getId())
                .currentStep("护理主管(" + user.getNickName() + ")评估")
                .nextStep("院长审批")
                .auditStatus(1)
                .stepNo("1")
                .handleType(0)
                .build();
        accraditationRecordMapper.save(accraditationRecord);
    }

    /**
     * 院长审批
     * @param checkInId
     * @param taskId
     * @param message
     */
    @Override
    public void agree(Long checkInId, String taskId, String message) {
        // 1、修改checkIn表的状态
        CheckIn checkIn = new CheckIn();
        checkIn.setId(checkInId);
        checkIn.setFlowStatus(CheckIn.FlowStatus.APPROVAL.getCode());
        checkInMapper.update(checkIn);

        // 2、审批通过
        workFlowService.complete(taskId, message,1);

        // 3、保存审核记录
        LoginUserDetails user = UserThreadLocal.getUser();

        // 查询这个任务第一步的养老顾问的信息
        // 思路：查询历史任务，从历史变量中取取出来
        Long userId = workFlowService.getGuwen(taskId,"user0");
        User user2 = userMapper.getUserById(userId);

        AccraditationRecord accraditationRecord = AccraditationRecord.builder()
                .opinion("护理主管审批-同意")
                .type(3)
                .approverId(user.getId())
                .approverName(user.getRealName())
                .nextApproverId(user2.getId())
                .nextApprover(user2.getRealName())
                .bussniessId("bussiness:"+checkInId)
                .currentStep("院长审批(" + user.getNickName() + ")")
                .nextStep("养老顾问（"+user2.getRealName()+"）分配房间")
                .auditStatus(1)
                .stepNo("2")
                .handleType(0)
                .build();
        accraditationRecordMapper.save(accraditationRecord);
    }

    /**
     * 院长审批-拒绝
     * @param checkInId
     * @param taskId
     * @param message
     */
    @Override
    public void reject(Long checkInId, String taskId, String message) {
        // 更新checkIn
        CheckIn checkIn = new CheckIn();
        checkIn.setId(checkInId);
        checkIn.setFlowStatus(CheckIn.FlowStatus.APPROVAL.getCode());
        checkIn.setStatus(CheckIn.Status.CLOSED.getCode());
        checkInMapper.update(checkIn);

        // 执行工作流
        workFlowService.complete(taskId, message,2);

        LoginUserDetails user = UserThreadLocal.getUser();

        // 保存操作记录
        AccraditationRecord accraditationRecord = AccraditationRecord.builder()
                .opinion(message)
                .type(3)
                .approverId(user.getId())
                .approverName(user.getRealName())
                .nextApproverId(null)
                .nextApprover(null)
                .bussniessId("bussiness:"+checkInId)
                .currentStep("院长审批(" + user.getNickName() + ")-拒绝")
                .nextStep("")
                .auditStatus(2)
                .stepNo("2")
                .handleType(0)
                .build();
        accraditationRecordMapper.save(accraditationRecord);

    }

    // 院长审批-驳回

    /**
     * 院长审批的是前两步提交的数据，保留顾问填写的数据，这里要是不对，可以修改；但是流程会回到第一步
     * @param checkInId
     * @param taskId
     * @param message
     */
    @Override
    public void rebut(Long checkInId, String taskId, String message) {
        // 更新checkIn的数据
        CheckIn checkIn = new CheckIn();
        checkIn.setId(checkInId);
        checkIn.setFlowStatus(CheckIn.FlowStatus.APPLY.getCode());
        checkInMapper.update(checkIn);

        // 完成工作流
        workFlowService.complete(taskId,"驳回",3);

        // 保存操作历史
        LoginUserDetails user = UserThreadLocal.getUser();

        // 上一步谁审核的，这一步还是那个人审核,查询user1变量
        Long userId = workFlowService.getGuwen(taskId,"user1");
        User user1 = userMapper.getUserById(userId);

        // 保存操作记录
        AccraditationRecord accraditationRecord = AccraditationRecord.builder()
                .opinion(message)
                .type(3)
                .approverId(user.getId())
                .approverName(user.getRealName())
                .nextApproverId(user1.getId())
                .nextApprover(user1.getRealName())
                .bussniessId("bussiness:"+checkInId)
                .currentStep("院长审批(" + user.getNickName() + ")-驳回")
                .nextStep("护理主管（"+user1.getRealName()+"）-评估")
                .auditStatus(3)
                .stepNo("0")
                .handleType(0)
                .build();
        accraditationRecordMapper.save(accraditationRecord);
    }

    @Autowired
    CheckInConfigMapper checkInConfigMapper;

    @Autowired
    BedMapper bedMapper;
    /**
     * 养老顾问-入住选配
     * @param dto
     */
    @Override
    public void config(CheckInConfigDto dto) {
        LoginUserDetails user = UserThreadLocal.getUser();
        // 1、保存入住选配信息check_in_config
        CheckInConfig checkInConfig = new CheckInConfig();
        BeanUtils.copyProperties(dto,checkInConfig);
        checkInConfig.setCreateBy(user.getId());
        checkInConfig.setBedNo(dto.getBedNumber());
        checkInConfigMapper.save(checkInConfig);

        // 2、修改老人表，绑定床位
        ElderDto elder = elderMapper.findById(dto.getElderId());
        elder.setBedId(dto.getBedNo());
        elder.setBedNumber(dto.getBedNumber());
        elderMapper.update(elder);

//        if(count != 1) {
//            throw new RuntimeException("更新老人信息失败");
//        }

        // 3、修改checkIn表，流程状态、入住时间
        CheckIn checkIn = new CheckIn();
        checkIn.setId(dto.getCheckInId());
        checkIn.setFlowStatus(CheckIn.FlowStatus.CONFIG.getCode());
        checkIn.setCheckInTime(dto.getCheckInStartTime());
        checkInMapper.update(checkIn);

//        if(count != 1) {
//            throw new RuntimeException("更新老人信息失败");
//        }
        // 4、修改床位表，设置被占用
        Bed bed = new Bed();
        bed.setId(dto.getBedNo());
        bed.setBedStatus(1);
        bedMapper.update(bed);

//        if(count != 1) {
//            throw new RuntimeException("更新老人信息失败");
//        }
        // 5、完成任务
        workFlowService.complete(dto.getTaskId(),"选配完成",null);
        User user1 = userMapper.findUsernameByDeptNo(RetreatConstant.LEGAL_DEPT_CODE);
        // 6、保存操作记录
        AccraditationRecord accraditationRecord = AccraditationRecord.builder()
                .opinion("选配完成")
                .type(3)
                .approverId(user.getId())
                .approverName(user.getRealName())
                .nextApproverId(user1.getId())
                .nextApprover(user1.getRealName())
                .bussniessId("bussiness:"+dto.getCheckInId())
                .currentStep("养老顾问(" + user.getNickName() + ")-选配")
                .nextStep("法务部（"+user1.getRealName()+"）-评估")
                .auditStatus(1)
                .stepNo("3")
                .handleType(0)
                .build();
        accraditationRecordMapper.save(accraditationRecord);
    }

    @Autowired
    NursingPlanMapper nursingPlanMapper;

    @Override
    public void sign(SignDto signDto) {

        // 1、checkIn流程状态往后推进
        CheckIn checkIn = checkInMapper.findById(signDto.getCheckInId());
        checkIn.setCheckInTime(signDto.getStartTime());
        checkIn.setFlowStatus(CheckIn.FlowStatus.SIGN.getCode());
        checkInMapper.update(checkIn);

        // 2、生成护理任务
        // 思路：选择的护理计划是哪个，获取这个护理计划下的所有护理项目，生成护理任务
        // 不明确的需求，多查阅需求文档，原型图一般都配置的有说明；还不清楚，需要给产品经理沟通
        Long elderId = checkIn.getElderId();
        ElderDto elder = elderMapper.findById(elderId);
        // 根据老人id查询护理等级id
        Long levelId = checkInConfigMapper.findLevelIdByElderId(elderId);
        List<NursingProjectPlan> nursingPlans = nursingPlanMapper.findNursingTaskByLevelId(levelId);
        for (NursingProjectPlan nursingPlan : nursingPlans) {
            NursingTask nursingTask = new NursingTask();
            // 写一个算法来协调资源，6个护理员，500人
            // 找一个当前任务最少的人，把任务分配给他
            nursingTask.setNursingId(null);
            nursingTask.setProjectId(nursingPlan.getProjectId());
            nursingTask.setElderId(elderId);
            nursingTask.setBedNumber(elder.getBedNumber());
            nursingTask.setTaskType(2L);
            // 考虑：不同的任务执行时间不一样。
            nursingTask.setEstimatedServerTime(new Date());
            nursingTask.setStatus(1);
            nursingTask.setRelNo("ZD");
//            nursingTaskMapper.save(nursingTask);
        }

        // 3、更新老人状态

        // 4、保存合同信息

        // 完成任务

        // 保存历史记录
    }
}
